﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
using EFCore读写分离.Common;
using EFCore读写分离.IService;
using EFCore读写分离.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Query;

namespace EFCore读写分离.Services
{
    /// <summary>
    /// 读写分离的情况下，所有的Attach状态都会失效
    /// </summary>
    public class BaseService : IBaseService
    {
        public DbApplicationContext DbContext { get; set; }

        public BaseService(DbApplicationContext dbContext)
        {
            DbContext = dbContext;
        }

        public T SelectOne<T>(Expression<Func<T, bool>> condition) where T : class
        {
            DbContext.ToSlave();
            if(condition == null)
                return DbContext.Set<T>().AsNoTracking().FirstOrDefault();
            return DbContext.Set<T>().AsNoTracking().Where(condition).FirstOrDefault();
        }

        public List<T> SelectList<T>(Expression<Func<T, bool>> condition) where T : class
        {
            DbContext.ToSlave();
            if(condition == null)
                return DbContext.Set<T>().ToList();
            return DbContext.Set<T>().AsNoTracking().Where(condition).ToList();
        }

        public PageResult<T> SelectPage<T, S>(Expression<Func<T, bool>> condition, int pageIndex, int pageSize, Expression<Func<T, S>> orderbyfield, bool isAsc = true) where T : class
        {
            DbContext.ToSlave();
            DbSet<T> list = DbContext.Set<T>();
            if(condition != null)
                list.Where(condition);
            if(orderbyfield != null)
            {
                if (isAsc)
                {
                    list.OrderBy(orderbyfield);
                }
                else
                {
                    list.OrderByDescending(orderbyfield);
                }
            }

            return new PageResult<T>()
            {
                PageIndex = pageIndex,
                PageSize = pageSize,
                DataList = list.Skip(pageIndex).Take(pageSize).ToList(),
                TotalCount = list.Count(condition)
            };
        }

        public T Insert<T>(T entity) where T : class
        {
            DbContext.ToMaster();
            DbContext.Set<T>().Add(entity);
            DbContext.SaveChanges();
            return entity;
        }

        public int InsertRange<T>(IEnumerable<T> entities) where T : class
        {
            DbContext.ToMaster();
            DbContext.AddRange(entities);
            return DbContext.SaveChanges();
        }

        public void Update<T>(T entity) where T : class
        {
            DbContext.ToMaster();
            //DbContext.Set<T>().Attach(entity);   //将数据附加到上下文，支持实体修改和新实体，重置为UnChanged
            //DbContext.Entry(entity).State = Microsoft.EntityFrameworkCore.EntityState.Modified;
            DbContext.Set<T>().Update(entity);
            DbContext.SaveChanges();
        }

        public int UpdateRange<T>(IEnumerable<T> entities) where T : class
        {
            DbContext.ToMaster();
            DbContext.Set<T>().UpdateRange(entities);
            return DbContext.SaveChanges();
        }

        /// <summary>
        /// 用法说明：  
        ///     dbContext.Products
        ///              .Where(p => p.StockQuantity< 10)
        ///              .ExecuteUpdate(setters => setters
        ///                  .SetProperty(p => p.Status, "OutOfStock")
        ///                  .SetProperty(p => p.UpdatedAt, DateTime.UtcNow)
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="condition"></param>
        /// <param name="setPropertyCalls"></param>
        /// <returns></returns>
        public int UpdateRange<T>(Expression<Func<T,bool>> condition,Expression<Func<SetPropertyCalls<T>, SetPropertyCalls<T>>> setPropertyCalls) where T : class
        {
            DbContext.ToMaster();
            return DbContext.Set<T>()
                    .Where(condition)
                    .ExecuteUpdate(setPropertyCalls);
        }

        public void Delete<T>(T entity) where T : class
        {
            DbContext.ToMaster();
            DbContext.Set<T>().Remove(entity);
            DbContext.SaveChanges();
        }

        public int DeleteRange<T>(IEnumerable<T> entities) where T : class
        {
            DbContext.ToMaster();
            DbContext.Set<T>().RemoveRange(entities);
            return DbContext.SaveChanges();
        }

        public int DeleteRange<T>(Expression<Func<T, bool>> condition) where T : class
        {
            DbContext.ToMaster();
            return DbContext.Set<T>().Where(condition).ExecuteDelete();
        }
    }
}
